#include "tetris.h"

#include <stdlib.h>

#include "bt.h"
#include "oled.h"
#include "ws2812.h"

#undef Handler
#define Handler Tetris_HandleTypeDef*

#undef handler
#define handler (**(Tetris_HandleTypeDef**)&handle)

const uint8_t color[7][3] = {
    {0x03, 0x01, 0x03},  // 淡蓝, I
    {0x01, 0x00, 0x02},  // 深蓝, J
    {0x01, 0x03, 0x01},  // 橙色, L
    {0x03, 0x03, 0x00},  // 黄色, O
    {0x03, 0x00, 0x00},  // 绿色, S
    {0x00, 0x03, 0x00},  // 红色, Z
    {0x03, 0x03, 0x03}   // 紫色, T
};
// 低位在4*4矩阵的左上角, 高位在右下角, 列行式
// 第一个是正向的状态, 然后依次顺时针旋转
const uint16_t form[7][4] = {
    {0x4444, 0x0F00, 0x2222, 0x00F0},  // I
    {0x4460, 0x02E0, 0x0622, 0x0740},  // J
    {0x0644, 0x0047, 0x0226, 0x00E2},  // L
    {0x0066, 0x0066, 0x0066, 0x0066},  // O
    {0x0264, 0x0630, 0x2640, 0x0C60},  // S
    {0x0462, 0x0360, 0x4620, 0x06C0},  // Z
    {0x0464, 0x0270, 0x2620, 0x0E40},  // T
};

void Tetris_Init(void* handle, uint8_t speed) {
  Oled_ClearAll();
  Oled_ShowStringBig(0, 0, "     TETRIS     ");
  Bt_Init(Tetris_M_L, NULL, NULL, Tetris_M_O,    // M
          Tetris_U_LJ, NULL, Tetris_U_LJ, NULL,  // U
          Tetris_D_LJ, NULL, Tetris_D_LJ, NULL,  // D
          Tetris_L_LJ, NULL, Tetris_L_LJ, NULL,  // L
          Tetris_R_LJ, NULL, Tetris_R_LJ, NULL,  // R
          100);
  srand(hbt.time.now);
  handler.outo = 0;
  handler.form = 0;
  handler.type = 0;
  handler.next = 1;
  handler.x = rand() % 32;
  handler.y = -4;
  handler.score = 0;
  handler.timeout = (((uint16_t)(8 - speed)) * 256);
  handler.timeMoveDown = hbt.time.now;
  Ws_Init();
  Tetris_InitShow();
  Tetris_ShowScore(handle);
  Tetris_ShowNext(handle);
  while (hbt.bu & BU_M) Bt_Update();
}
void Tetris_InitShow(void) {
  Oled_ClearAll();
  turn = 0xFF;
  Oled_ShowStringBig(0, 0, "     TETRIS     ");
  turn = 0x00;
  Oled_ShowStringSmall(0, 2, "Score:");
  Oled_ShowStringSmall(0, 3, "x:");
  Oled_ShowStringSmall(0, 4, "y:");
  Oled_ShowStringSmall(64, 2, "Next:");
}

void Tetris_ShowCood(void* handle) {
  Oled_ShowNumSmall(12, 3, handler.x, 2);
  Oled_ShowNumSmall(12, 4, handler.y + 4, 2);
}

void Tetris_ShowScore(void* handle) {
  Oled_ShowNumSmall(40, 2, handler.score, 3);
}

void Tetris_ShowNext(void* handle) {
  uint8_t str[2][9];
  for (uint8_t i = 0; i < 4; i++) {
    for (uint8_t j = 0; j < 2; j++) {
      if (form[handler.next][0] & (0x0001 << ((i << 2) + j + 1))) {
        str[j][i << 1] = 0x7F;
        str[j][(i << 1) + 1] = 0x7F;
      } else {
        str[j][i << 1] = ' ';
        str[j][(i << 1) + 1] = ' ';
      }
    }
  }
  for (uint8_t j = 0; j < 2; j++) {
    str[j][8] = '\0';
    Oled_ShowStringBig(64, (j << 1) + 3, (char*)(str[j]));
  }
}

void Tetris_Judge(void* handle) {
  if (hbt.time.now - handler.timeMoveDown > (uint32_t)(handler.timeout << 1)) {
    // 方块已经2s没向下移动了, 代表这个方块已经到底了
    // 先判断是否得分
    uint8_t a = 0;
    for (uint8_t i = 0; i < 32; i++) {
      if (!(hws.matrix.color[i][7][0] || hws.matrix.color[i][7][1] ||
            hws.matrix.color[i][7][2]))
        a |= 1;
    }
    if (!a) {
      // 得分
      handler.score += 1;
      Tetris_ShowScore(handle);
      for (uint8_t i = 0; i < 32; i++) {
        for (uint8_t j = 7; j > 0; j--) {
          hws.matrix.color[i][j][0] = hws.matrix.color[i][j - 1][0];
          hws.matrix.color[i][j][1] = hws.matrix.color[i][j - 1][1];
          hws.matrix.color[i][j][2] = hws.matrix.color[i][j - 1][2];
        }
      }
      for (uint8_t i = 0; i < 32; i++) {
        hws.matrix.color[i][0][0] = 0x00;
        hws.matrix.color[i][0][1] = 0x00;
        hws.matrix.color[i][0][2] = 0x00;
      }
    }

    if (handler.y < -3 && (form[handler.type][handler.form] & 0x8888)) {
    } else if (handler.y < -2 && (form[handler.type][handler.form] & 0x4444)) {
    } else if (handler.y < -1 && (form[handler.type][handler.form] & 0x2222)) {
    } else if (handler.y < 0 && (form[handler.type][handler.form] & 0x1111)) {
    } else {
      // 创建新的方块
      handler.form = 0;
      handler.type = handler.next;
      handler.next = (handler.next + 1) % 7;
      handler.x = 14;
      handler.y = -4;
      handler.timeMoveDown = hbt.time.now;
      Tetris_ShowNext(handle);
      return;
    }
    // game over
    handler.outo = 1;
    Oled_ClearAll();
    Oled_ShowStringBig(24, 3, "GAME  OVER");
    HAL_Delay(1000);
  } else if (hbt.time.now - handler.timeMoveDown > handler.timeout) {
    // 尝试将方块下移
    Tetris_D_LJ(handle);
  }
}

void Tetris_Run(void* handle) {
  // 将这俩个语句放在主循环之前, 防止游戏开始时由于超时而直接退出
  Bt_Update();
  handler.timeMoveDown = hbt.time.now;
  while (1) {
    Bt_Update();
    Bt_Run(handle);
    Tetris_ShowCood(handle);
    Tetris_Judge(handle);
    if (handler.outo) {
      for (uint8_t i = 0; i < 32; i++) {
        for (uint8_t j = 0; j < 8; j++) {
          hws.matrix.color[i][j][0] = 0x00;
          hws.matrix.color[i][j][1] = 0x05;
          hws.matrix.color[i][j][2] = 0x00;
        }
      }
      Ws_Load(hws.matrix.color);
      Ws_Draw();
      return;
    }
  }
}

uint8_t Tetris_Drawable(void* handle) {
  // 判断位置是否可用
  if (handler.y > 7 && (form[handler.type][handler.form] & 0x1111)) {
    return 0;
  } else if (handler.y > 6 && (form[handler.type][handler.form] & 0x2222)) {
    return 0;
  } else if (handler.y > 5 && (form[handler.type][handler.form] & 0x4444)) {
    return 0;
  } else if (handler.y > 4 && (form[handler.type][handler.form] & 0x8888)) {
    return 0;
  }
  for (int8_t i = 0; i < 4; i++) {
    for (int8_t j = 0; j < 4; j++) {
      if (handler.y + j < 0 || handler.y + j > 7) {
        continue;
      }
      if ((hws.matrix.color[(handler.x + i) & 0x1F][handler.y + j][0] ||
           hws.matrix.color[(handler.x + i) & 0x1F][handler.y + j][1] ||
           hws.matrix.color[(handler.x + i) & 0x1F][handler.y + j][2]) &&
          (form[handler.type][handler.form] & (0x0001 << ((i << 2) + j)))) {
        return 0;
      }
    }
  }
  return 1;
}

void Tetris_EraseBlock(void* handle) {
  for (int8_t i = 0; i < 4; i++) {
    for (int8_t j = 0; j < 4; j++) {
      if (handler.y + j < 0 || handler.y + j > 7) {
        continue;
      }
      if (form[handler.type][handler.form] & (0x0001 << ((i << 2) + j))) {
        hws.matrix.color[(handler.x + i) % 32][handler.y + j][0] = 0;
        hws.matrix.color[(handler.x + i) % 32][handler.y + j][1] = 0;
        hws.matrix.color[(handler.x + i) % 32][handler.y + j][2] = 0;
      }
    }
  }
  Ws_Load(hws.matrix.color);
  Ws_Draw();
}

// 试图将一个图像显示在指定位置, 如果位置不可用则返回1
// 注意: y坐标可以为负数
void Tetris_DrawBlock(void* handle) {
  // 将图像绘制在指定位置
  for (int8_t i = 0; i < 4; i++) {
    for (int8_t j = 0; j < 4; j++) {
      if (handler.y + j < 0 || handler.y + j > 7) {
        continue;
      }
      if (form[handler.type][handler.form] & (0x0001 << ((i << 2) + j))) {
        hws.matrix.color[(handler.x + i) % 32][handler.y + j][0] =
            color[handler.type][0];
        hws.matrix.color[(handler.x + i) % 32][handler.y + j][1] =
            color[handler.type][1];
        hws.matrix.color[(handler.x + i) % 32][handler.y + j][2] =
            color[handler.type][2];
      }
    }
  }
  Ws_Load(hws.matrix.color);
  Ws_Draw();
}

void Tetris_M_L(void* handle) { handler.outo = 1; }
void Tetris_M_O(void* handle) {
  // 游戏暂停
  turn = 0xFF;
  Oled_ShowStringBig(0, 6, "PAUSE");
  turn = 0x00;
  while (hbt.bu & BU_M) Bt_Update();
  HAL_Delay(10);
  // 此时所有按键全部松开
  while (1) {
    Bt_Update();
    if (hbt.bu & BU_M_M) {
      // M键按下, 游戏继续
      while (hbt.bu & BU_M) Bt_Update();
      Bt_Update();
      Oled_ShowStringBig(0, 6, "     ");
      handler.timeMoveDown = hbt.time.now;
      return;
    }
  }
}
void Tetris_U_LJ(void* handle) {
  Tetris_EraseBlock(handle);
  handler.form++;
  if (Tetris_Drawable(handle)) {
    Tetris_DrawBlock(handle);
  } else {
    handler.form--;
    Tetris_DrawBlock(handle);
  }
}
void Tetris_D_LJ(void* handle) {
  Tetris_EraseBlock(handle);
  handler.y++;
  if (Tetris_Drawable(handle)) {
    Tetris_DrawBlock(handle);
    handler.timeMoveDown = hbt.time.now;
  } else {
    handler.y--;
    Tetris_DrawBlock(handle);
  }
}
void Tetris_L_LJ(void* handle) {
  Tetris_EraseBlock(handle);
  handler.x--;
  if (Tetris_Drawable(handle)) {
    Tetris_DrawBlock(handle);
  } else {
    handler.x++;
    Tetris_DrawBlock(handle);
  }
}
void Tetris_R_LJ(void* handle) {
  Tetris_EraseBlock(handle);
  handler.x++;
  if (Tetris_Drawable(handle)) {
    Tetris_DrawBlock(handle);
  } else {
    handler.x--;
    Tetris_DrawBlock(handle);
  }
}
